# central settings
#
simulation     = False
year           = "2012"
magnetPolarity = "Down"
###


########################################################################
from os import environ
import GaudiKernel.SystemOfUnits as Units 
from Gaudi.Configuration import *
#from Hlt.Configuration import *
from Configurables import (GaudiSequencer,
                           DecayTreeTuple,
                           CombineParticles,
                           LoKi__Hybrid__TupleTool,
                           DaVinci,
                           FilterDesktop)

from Configurables import LoKi__Hybrid__PlotTool as PlotTool 
from Configurables import LoKi__Hybrid__FilterCriterion as LoKiFilterCriterion
from DecayTreeTuple.Configuration import *

from GaudiConfUtils.ConfigurableGenerators import  CombineParticles
from PhysSelPython.Wrappers import (SelectionSequence, Selection, DataOnDemand, AutomaticData, MergedSelection)
from Configurables import LoKi__VoidFilter as Filter 
from PhysConf.Filters import LoKi_Filters
LoKiTool = LoKi__Hybrid__TupleTool( 'LoKiTool')
#
######################################################################################################

if simulation is True: 
    location = 'AllStreams/Phys/FullDSTDiMuonJpsi2MuMuDetachedLine/Particles'#MC的TES位置
if simulation is False:
    location = 'Dimuon/Phys/FullDSTDiMuonJpsi2MuMuDetachedLine/Particles'#data的TES位置
######################################################################################################
# if you are setting something wrong, it should die here...
if year not in [ "2011", "2012" ]:
    raise RuntimeError(year + " is not a valid year")
if magnetPolarity not in ["Up", "Down"]:
    raise RuntimeError(magnetPolarity + " is not a valid magnet polarity")
######################################################################################################
# CUTS CUTS CUTS CUTS CUTS CUTS CUTS
####################################
BdCombCuts = "(AM > 4500 * MeV) & "\
            "(AM < 7100 * MeV)" 
BdCuts = "(M                                   > 4600 * MeV) & "\
    "(M                                   < 7000 * MeV) & "\
    "(BPVIPCHI2()                         < 16) & "\
    "(BPVDIRA                             > 0.9995) & "\
    "(BPVVDCHI2                           > 64) & "\
    "(MAXTREE(ISBASIC,MIPCHI2DV(PRIMARY)) > 9 ) & "\
    "(VFASPF(VCHI2/VDOF)                  < 10)"

MuonCuts = "(TRGHP < 0.5) & "\
          "(MIPCHI2DV(PRIMARY) > 9) & "\
          "(PIDmu> 0) & "\
          "(ISMUON)"

Pi0Cuts = "(PT > 300) &"\
    "(CL > 0.02)"

#######################################################################################################
"""
def __Pions__(PionCuts):
    _pions = AutomaticData(Location = 'Phys/StdAllLoosePions/Particles')#从给定的地址中获取PI的粒子数据
    _filter = FilterDesktop("PionFilter", Code = PionCuts)#创建一个名为"PionFilter"的筛选器，cut为PionCuts
    _sel = Selection("Selection_StdAllLoosePions",
                     RequiredSelections = [ _pions ] ,
                     Algorithm = _filter)#使用 FilterDesktop 筛选器对 StdLoosePions 进行筛选，并创建一个名为 "Selection_StdAllLoosePions" 的选择对象。
    return _sel#返回创建的选择对象 _sel。
"""
###
def __Pi0__(Pi0Cuts):
    _pi0resolved = AutomaticData(Location = 'Phys/StdLooseResolvedPi0/Particles')
    _pi0merged = AutomaticData(Location = 'Phys/StdLooseMergedPi0/Particles')
    _filter_pi0resolved = FilterDesktop("ResolvedPi0Filter", Code = Pi0Cuts )
    _filter_pi0merged = FilterDesktop("MergedPi0Filter", Code = Pi0Cuts  )        
    _selpi0resolved = Selection("Selection_pi0resolved",
                                RequiredSelections = [ _pi0resolved ] ,
                                Algorithm = _filter_pi0resolved)
    _selpi0merged = Selection("Selection_pi0merged",
                              RequiredSelections = [ _pi0merged ] ,
                              Algorithm = _filter_pi0merged)
    _sel = MergedSelection("Selection_pi0",
                           RequiredSelections = [ _selpi0resolved,_selpi0merged ])#使用 MergedSelection 将经过筛选的标准解析π^0子和标准合并π^0子合并为一个名为 "Selection_pi0" 的选择对象。
    return _sel
###
"""
def __Omega2PiPiPi0__(Pions, Pi0, RhoCuts):
    _omega2pipipizero = CombineParticles()
    _omega2pipipizero.DecayDescriptor = "[omega(782) -> pi+ pi- pi0]cc"
    _omega2pipipizero.MotherCut = OmegaCuts
    
    _omegaConf = _omega2pipipizero.configurable("Combine_PiPiPi0")
    
    _selOMEGA2PIPIPIZERO = Selection( "Selection_omega2pipipizero",
                                  Algorithm = _omegaConf,
                                  RequiredSelections = [ Pions, Pi0 ] )
    return _selOMEGA2PIPIPIZERO
###
"""
def __jpsi__(location):
    return AutomaticData(Location = location)
###
def __jpsipi0__(Jpsi, BdCombCuts, BdCuts):#这是一个函数定义，它接受四个参数 Jpsi, Omega, BdCombCuts, BdCuts。
    _jpsipi0 = CombineParticles()#创建一个组合粒子对象 _jpsiomega，用于将 J/ψ子和 Omega介子组合成 B^0 (Bd) 介子。
    _jpsipi0.DecayDescriptor = "[B0 -> J/psi(1S) pi0]cc"#设置 B^0 (Bd) 介子的衰变描述符，描述了 B^0 (Bd) 介子的衰变方式为包含 J/ψ子和 Omega介子。
    _jpsipi0.CombinationCut = BdCombCuts#设置 B^0 (Bd) 介子的组合条件，这些条件可能包括不变质量、顶点位置等。
    _jpsipi0.MotherCut = BdCuts#设置 B^0 (Bd) 介子的母粒子条件，用于筛选 B^0 (Bd) 介子的属性。
    _jpsipi0Conf = _jpsipi0.configurable("Combine_BJpsipi0")#创建一个可配置的组合粒子对象 _jpsiomegaConf，用于后续的操作。

    _selJpsipi0 = Selection("BJpsipi0",
                              Algorithm = _jpsipi0Conf,
                              RequiredSelections = [ Jpsi, Pi0])#创建一个选择对象 _selJpsiOmega，该对象用于对 B^0 (Bd) 介子进行选择，其中包括组合粒子对象 _jpsiomega 以及 J/ψ子和 Omega介子作为必需的选择
                                
    return _selJpsipi0
#######
#Pions           = __Pions__(PionCuts)#这个函数调用是用来获取符合特定切割条件的π介子
Pi0             = __Pi0__(Pi0Cuts)#这个函数调用是用来获取符合特定切割条件的π^0介子。
#Omega           = __Omega2PiPiPi0__(Pions, Pi0, OmegaCuts)#这个函数调用是用来将π介子和π^0介子组合成Ω介子，同时应用特定的切割条件。
Jpsi            = __jpsi__(location)#这个函数调用是用来获取来自stripping line的J/ψ子，其中位置参数可能是针对数据和MC不同的位置。
Jpsipi0   = __jpsipi0__(Jpsi,BdCombCuts, BdCuts)#这个函数调用是用来将J/ψ子和Ω介子组合成B^0 (Bd)介子，同时应用特定的组合切割条件和母粒子切割条件。
###
BJpsipi0Seq = SelectionSequence( 'BJpsipi0Seq', TopSelection = Jpsipi0 )
#这行代码创建了一个名为 "BJpsiOmegaSeq" 的选择序列，该序列包含了之前定义的 "JpsiOmega" 选择对象作为顶层选择。
#选择序列通常用于将多个选择对象组合在一起，以构建复杂的分析流程。在这种情况下，"BJpsiOmegaSeq" 序列将 "JpsiOmega" 选择对象作为顶层选择，可能是为了进一步的分析或后续的处理步骤
######################################################################################################
BJpsiOmega1 = DecayTreeTuple("BJpsiOmegaTuple1")

# input locations
BJpsiOmega1.Inputs = [ Jpsipi0.outputLocation() ]


# decay descriptors
# first is for resolved pi0
# second is for merged pi0
BJpsiOmega1.Decay = "[B0 -> ^(J/psi(1S) -> ^mu+ ^mu-) ^(pi0 -> ^gamma ^gamma)]CC"

# define the tools and configure them
BJpsiOmega1.ToolList = [
    "TupleToolKinematic"
    ,"TupleToolPid"
    ,"TupleToolANNPID"
    ,"TupleToolTrackInfo"
    ,"TupleToolRecoStats"
    #,"TupleToolTrigger"
    ,"TupleToolPrimaries"
    ]



# define the list of triggers that could have fired...
triggerList = [ "L0HadronDecision", 
                "L0DiMuonDecision", 
                "L0MuonDecision", 
                "Hlt1TrackMuonDecision", 
                "Hlt1TrackAllL0Decision", 
                "Hlt1DiMuonLowMassDecision", 
                "Hlt1DiMuonHighMassDecision", 
                "Hlt1SingleMuonHighPTDecision", 
                "Hlt2Topo2BodySimpleDecision",
                "Hlt2Topo3BodySimpleDecision",
                "Hlt2Topo4BodySimpleDecision",
                "Hlt2Topo2BodyBBDTDecision",
                "Hlt2Topo3BodyBBDTDecision",
                "Hlt2Topo4BodyBBDTDecision",
                "Hlt2TopoMu2BodyBBDTDecision",
                "Hlt2TopoMu3BodyBBDTDecision",
                "Hlt2TopoMu4BodyBBDTDecision",
                "Hlt2TopoE2BodyBBDTDecision",
                "Hlt2TopoE3BodyBBDTDecision",
                "Hlt2TopoE4BodyBBDTDecision",
                "Hlt2DiMuonDetachedHeavyDecision", 
                "Hlt2SingleMuonDecision", 
                "Hlt2DiMuonDetachedDecision", 
                "Hlt2DiMuonDetachedJPsiDecision", 
                "Hlt2DiMuonDetachedHeavyDecision" ]

# other variables
# including isolation variables
# for microDST, they are stored under a special location
BJpsiOmega1.addTupleTool("LoKi::Hybrid::TupleTool/LoKiTool")
BJpsiOmega1.LoKiTool.Variables = {
    "eta"                     : "ETA",
    "phi"                     : "PHI",
    "LV01"                    : "LV01",
    "BPVDLS"                  : "BPVDLS" }



# some truthmatching stuff
if simulation is True:
    MCTruth1=BJpsiOmega1.addTupleTool("TupleToolMCTruth/MCTruth1")
    MCTruth1.addTupleTool("MCTupleToolHierarchy")
    

####

####

################################################################
################################################################
# define the branches for resolved pi0
# for resolved pi0
BJpsiOmega1.addBranches({  # remove all "^" except where needed.
        "B"          : "^[B0 -> (J/psi(1S) ->  mu+  mu-) ( pi0 ->  gamma  gamma)]CC",
        "muplus"     : "  [B0 -> (J/psi(1S) ->  mu+ ^mu-) ( pi0 ->  gamma  gamma)]CC",
        "muminus"    : "  [B0 -> (J/psi(1S) -> ^mu+  mu-) ( pi0 ->  gamma  gamma)]CC",
        "Jpsi"       : "  [B0 -> ^(J/psi(1S) -> mu+  mu-) ( pi0 ->  gamma  gamma)]CC",
        "pi0Resolved": "  [B0 -> (J/psi(1S) ->  mu+  mu-) ^(pi0 ->  gamma  gamma)]CC",
        "gamma1"     : "  [B0 -> (J/psi(1S) ->  mu+  mu-) ( pi0 -> ^gamma  gamma)]CC",
        "gamma2"     : "  [B0 -> (J/psi(1S) ->  mu+  mu-) ( pi0 ->  gamma ^gamma)]CC"
        })

### B tools
BJpsiOmega1.B.addTupleTool("TupleToolDecayTreeFitter/BDTF")
BJpsiOmega1.B.BDTF.constrainToOriginVertex = True
BJpsiOmega1.B.BDTF.daughtersToConstrain = [ 'J/psi(1S)', 'pi0' ]
BJpsiOmega1.B.addTupleTool("TupleToolGeometry")
BJpsiOmega1.B.addTupleTool("TupleToolPropertime")
BJpsiOmega1.B.addTupleTool( "TupleToolTISTOS/TupleToolTISTOS")
BJpsiOmega1.B.TupleToolTISTOS.TriggerList = triggerList 
BJpsiOmega1.B.TupleToolTISTOS.VerboseL0 = True
BJpsiOmega1.B.TupleToolTISTOS.VerboseHlt1 = True
BJpsiOmega1.B.TupleToolTISTOS.VerboseHlt2 = True
BJpsiOmega1.B.addTupleTool("TupleToolVtxIsoln")
BJpsiOmega1.B.addTupleTool("TupleToolConeIsolation/TupleToolConeIsolation")
BJpsiOmega1.B.TupleToolConeIsolation.Verbose = True
BJpsiOmega1.B.TupleToolConeIsolation.MinConeSize = 0.6
BJpsiOmega1.B.TupleToolConeIsolation.MaxConeSize = 2.0
BJpsiOmega1.B.TupleToolConeIsolation.SizeStep = 0.3



### Jpsi tools
BJpsiOmega1.Jpsi.addTupleTool("TupleToolGeometry")
BJpsiOmega1.Jpsi.addTupleTool( "TupleToolTISTOS/TupleToolTISTOS")
BJpsiOmega1.Jpsi.TupleToolTISTOS.TriggerList = triggerList 
BJpsiOmega1.Jpsi.TupleToolTISTOS.VerboseL0 = True
BJpsiOmega1.Jpsi.TupleToolTISTOS.VerboseHlt1 = True
BJpsiOmega1.Jpsi.TupleToolTISTOS.VerboseHlt2 = True

# muplus tools
BJpsiOmega1.muplus.addTupleTool("TupleToolGeometry")
BJpsiOmega1.muplus.addTupleTool( "TupleToolTISTOS/TupleToolTISTOS")
BJpsiOmega1.muplus.TupleToolTISTOS.TriggerList = triggerList
BJpsiOmega1.muplus.TupleToolTISTOS.VerboseL0 = True
BJpsiOmega1.muplus.TupleToolTISTOS.VerboseHlt1 = True
BJpsiOmega1.muplus.TupleToolTISTOS.VerboseHlt2 = True

# muminus tools
BJpsiOmega1.muminus.addTupleTool("TupleToolGeometry")
BJpsiOmega1.muminus.addTupleTool( "TupleToolTISTOS/TupleToolTISTOS")
BJpsiOmega1.muminus.TupleToolTISTOS.TriggerList = triggerList
BJpsiOmega1.muminus.TupleToolTISTOS.VerboseL0 = True
BJpsiOmega1.muminus.TupleToolTISTOS.VerboseHlt1 = True
BJpsiOmega1.muminus.TupleToolTISTOS.VerboseHlt2 = True


# resolved pi0 tools
BJpsiOmega1.pi0Resolved.addTupleTool("TupleToolPi0Info")
#BJpsiOmega1.pi0Resolved.addTupleTool("TupleToolVeto/veto")
#BJpsiOmega1.pi0Resolved.veto.Particle = "pi0"
#BJpsiOmega1.pi0Resolved.veto.VetoOther["Pi0R"]=["Phys/StdLooseResolvedPi0/Particles"]
#BJpsiOmega1.pi0Resolved.veto.OutputLevel = 3

# gamma tools
BJpsiOmega1.gamma1.addTupleTool("TupleToolPhotonInfo")
BJpsiOmega1.gamma1.addTupleTool("TupleToolCaloHypo")

# gamma tools
BJpsiOmega1.gamma2.addTupleTool("TupleToolPhotonInfo")
BJpsiOmega1.gamma2.addTupleTool("TupleToolCaloHypo")

################################################################
################################################################
# define the branches for merged pi0
#


################################################################
################################################################
Seq1 = GaudiSequencer("Seq1")#创建了一个名为 "Seq1" 的 GaudiSequencer 对象
Seq1.Members.append( BJpsipi0Seq.sequence() )#将 "BJpsiOmegaSeq.sequence()" 添加到了 "Seq1" 的成员列表中。这表示将 "BJpsiOmegaSeq" 中的序列添加到 "Seq1" 中
Seq1.Members.append( BJpsiOmega1 )#将 "BJpsiOmega1" 和 "BJpsiOmega2" 两个对象也添加到了 "Seq1" 的成员列表中。
Seq1.ModeOR = True#设置了 "Seq1" 的 ModeOR 属性为 True。这表示如果其中一个成员通过了要求，整个 Sequencer 将被标记为通过
Seq1.ShortCircuit = False#设置了 "Seq1" 的 ShortCircuit 属性为 False。这表示即使其中一个成员未通过要求，整个 Sequencer 也会继续运行。
# ######################################################################################################
DaVinci().MainOptions = ""#DaVinci().MainOptions = "" - 这行代码清空了 DaVinci 主选项设置，这意味着没有特定的主选项被设置
DaVinci().UserAlgorithms = [Seq1]#这行代码将之前创建的 "Seq1" 添加到了 DaVinci 的用户算法列表中，这表示 "Seq1" 中定义的分析流程将会被应用到 DaVinci 的数据处理过程中。
# ######################################################################################################
stripFilter = LoKi_Filters (
    STRIP_Code = " HLT_PASS_RE ('StrippingFullDSTDiMuonJpsi2MuMuDetachedLineDecision') "
)#这行代码定义了一个名为 "stripFilter" 的 LoKi 过滤器，其中包含了一个 STRIP_Code，用于过滤满足特定条件的事例。
stripFilterSeq = stripFilter.sequence( "StripFilter" )#stripFilterSeq = stripFilter.sequence( "StripFilter" ) - 这行代码将之前定义的 "stripFilter" 序列化为名为 "StripFilter" 的序列。
########################################################################
DaVinci().EvtMax    = -1
DaVinci().DataType  = year
DaVinci().PrintFreq = 1000
DaVinci().Lumi      = True
DaVinci().EventPreFilters = [ stripFilterSeq ]#将之前定义的 "stripFilterSeq" 添加到了 DaVinci 的事件预过滤器列表中。这意味着在 DaVinci 处理事件数据之前，将会应用 "stripFilterSeq" 中定义的过滤条件进行预过滤。

DaVinci().InputType = "DST"

if simulation is True:
    DaVinci().Simulation = True
    DaVinci().Lumi       = False
    DaVinci().TupleFile  = "BJpsipi0 " + year + magnetPolarity + " MC.root"
    
if simulation is False: 
    DaVinci().TupleFile = "BJpsipi0 " + year + magnetPolarity + " .root"

# db tags                                                                                                                                             
if simulation is True:
    if year is "2011":
        if magnetPolarity is "Down":
            DaVinci().CondDBtag = "sim-20130522-vc-md100"
            DaVinci().DDDBtag   = "dddb-20130929"
        if magnetPolarity is "Up":
            DaVinci().CondDBtag = "sim-20130522-vc-mu100"
            DaVinci().DDDBtag   = "dddb-20130929"

    if year is "2012":
        if magnetPolarity is "Down":
            DaVinci().CondDBtag = "sim-20130522-1-vc-md100"
            DaVinci().DDDBtag   = "dddb-20130929-1"
        if magnetPolarity is "Up":
            DaVinci().CondDBtag = "sim-20130522-1-vc-mu100"
            DaVinci().DDDBtag   = "dddb-20130929-1"

### mag polarity does not matter                                                                                                                       
if simulation is False:
    if year is "2011":
        DaVinci().CondDBtag = "cond-20141107"
        DaVinci().DDDBtag   = "dddb-20130929"

    if year is "2012":
        DaVinci().CondDBtag = "cond-20141107"
        DaVinci().DDDBtag   = "dddb-20130929-1"



####################################################
# local files for testing go here
####################################################
from Gaudi.Configuration import *
from GaudiConf import IOHelper

#2012年的data数据
IOHelper('ROOT').inputFiles([
'LFN:/lhcb/LHCb/Collision12/DIMUON.DST/00041836/0000/00041836_00001700_1.dimuon.dst'
], clear=True)
FileCatalog().Catalogs = ['xmlcatalog_file:2012_data_catalog.xml']

#2011年的data数据
IOHelper('ROOT').inputFiles([
'LFN:/lhcb/LHCb/Collision11/DIMUON.DST/00041840/0000/00041840_00003078_1.dimuon.dst'
], clear=True)
FileCatalog().Catalogs = ['xmlcatalog_file:2011_data_catalog.xml']

#


'''
IOHelper().inputFiles([
    './00041836_00001700_1.dimuon.dst'
    ], clear=True)
'''
